package ldapUtil

import (
	"fmt"
	"strings"

	"github.com/go-ldap/ldap/v3"
	"go-ezcode-admin/system/core/log"
	"go-ezcode-admin/system/util/str"
)

type LdapConfig struct {
	Host      string //"ldap://ip:389"
	User      string //"username"需要验证的用户
	Pass      string //需要验证的密码
	BaseDn    string //绑定形式验证就必须传 CN=administrator,CN=Users,DC=dcy,DC=dcy
	BindPass  string //绑定的密码
	SearchKey string //登陆时搜索的字段
}

type LdapUser struct {
	Name        string
	DisplayName string
	Email       string
}

//查询所有可用的用户
func (_this *LdapConfig) Users() (error, []LdapUser) {
	if _this.Host == "" || _this.User == "" || _this.Pass == "" || _this.BaseDn == "" {
		return fmt.Errorf("ldap config error"), nil
	}
	l, err := ldap.DialURL(_this.Host)
	if err != nil {
		return err, nil
	}
	defer l.Close()

	//Reconnect with TLS
	// err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// First bind with a read only user
	err = l.Bind(_this.User, _this.Pass)
	if err != nil {
		return err, nil
	}

	searchRequest := ldap.NewSearchRequest(
		_this.BaseDn, // The base dn to search
		2, ldap.NeverDerefAliases, 0, 0, false,
		"(&(objectClass=organizationalPerson))", // 搜素type
		[]string{"dn", "cn", "name", "email"},   //字段
		nil,
	)
	sr, err := l.Search(searchRequest)
	if err != nil {
		return err, nil
	}
	var users []LdapUser
	for _, entry := range sr.Entries {
		users = append(users, LdapUser{
			Name:        entry.GetAttributeValue("name"),
			DisplayName: entry.GetAttributeValue("cn"),
			Email:       entry.GetAttributeValue("email"),
		})
	}
	return nil, users
}

//验证用户，如果成功返回组
func (_this *LdapConfig) ValUserReturnMemberOf() (error, []string) {
	re := make([]string, 0)
	// 连接到LDAP服务器
	if !str.StartsWith(_this.Host, "ldap") {
		_this.Host = fmt.Sprintf("ldap://%s", _this.Host)
	}
	ldapConn, err := ldap.DialURL(_this.Host)
	if err != nil {
		log.Intrance().Error(err.Error())
		return fmt.Errorf("域服务器无法连接"), re
	}
	defer ldapConn.Close()

	// 进行身份验证（可选，根据需要）
	if _this.BindPass != "" {
		err = ldapConn.Bind(_this.BaseDn, _this.BindPass)
		if err != nil {
			return fmt.Errorf("管理账户LDAP身份验证失败"), re
		}
	}

	//逗号分割BaseDn
	arrDC := strings.Split(_this.BaseDn, ",")
	lenDc := len(arrDC)
	if lenDc < 2 {
		return fmt.Errorf("BaseDn配置错误"), re
	}
	beginDN := fmt.Sprintf("%s,%s", arrDC[lenDc-2], arrDC[lenDc-1])

	// 执行递归搜索以查找用户
	serachName := "cn" //默认
	if _this.SearchKey != "" {
		serachName = _this.SearchKey
	}
	searchRequest := ldap.NewSearchRequest(
		beginDN,                // 搜索的起始DN（根DN）
		ldap.ScopeWholeSubtree, // 搜索整个子树
		ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf("(%s=%s)", serachName, _this.User), // 使用通配符搜索用户名
		[]string{"dn", "cn", "memberOf"},               // 要检索的属性
		nil,
	)

	searchResult, err := ldapConn.Search(searchRequest)
	if err != nil {
		return fmt.Errorf("用户在域用户组无法找到"), re
	}
	if len(searchResult.Entries) == 0 {
		return fmt.Errorf("未找到用户 %s", _this.User), re
	}

	// 验证用户密码
	userDN := searchResult.Entries[0].DN
	err = ldapConn.Bind(userDN, _this.Pass)
	if err != nil {
		return fmt.Errorf("密码验证失败或账户已禁用"), re
	}

	// 提取用户的组成员身份
	userGroups := searchResult.Entries[0].GetAttributeValues("memberOf")
	//只提取用户组名称
	if len(userGroups) == 0 {
		return fmt.Errorf("未分配组"), re
	}

	for _, groupDN := range userGroups {
		LDN, _ := ldap.ParseDN(groupDN)
		if len(LDN.RDNs) == 0 {
			return fmt.Errorf("找不到组"), re
		}
		if len(LDN.RDNs[0].Attributes) == 0 {
			return fmt.Errorf("找不到组"), re
		}
		re = append(re, LDN.RDNs[0].Attributes[0].Value)

	}
	return nil, re

}
